#!/usr/bin/python

import pprint
import math

from sys import argv

# Some globals
diceSides = 6
debug = False

pp = pprint.PrettyPrinter(indent=4)

def getProbDicts(params):

    # Obtain a dict of all possible outcomes
    defOutcomes = getRawOutcomes(params['defDice'])

    # Determine the probabilities of specific MoS values
    successDict = getSuccessDistribution(params, params['threshold'], defOutcomes)

    # Evaluate the damage result of the MoS values
    damageDict = evaluateResults(params, successDict)

def evaluateResults(params, successDict):
    dam = params['dam']
    armor = params['armor']
    boxes = params['boxes']

    print "Evaluated threshold %i vs. %iD%i for damage %i vs. armor %i (overkill %i)" % (
        params['threshold'],
        params['defDice'], diceSides,
        params['dam'], params['armor'], params['boxes'] * params['armor']
        ),
    print ""

    labels = range(0, -6, -1)
    for label in labels:
        rngDict = successDict[label]
        if debug:
            print "Got (%s) dict = {%s}" % (label, rngDict)

        results = {}
        for x in rngDict.iterkeys():
            # x is a MoS
            prob = rngDict[x]
            damage = dam * float(x)
            res = math.floor(damage / armor)
            if debug:
                print "Res %f occurs from floor(%f x %i) = %f (%2.3f)" % (res, dam, x, res, prob)

            if res > boxes: 
                if debug:
                    print "Appending OK results (%6.3f) to bucket %i" % (prob, boxes)
                res = boxes

            if res in results:
                results[res] = results[res] + prob
            else:
                results[res] = prob
    
            if x not in results and x <= boxes:
                results[x] = 0.0

        print "{%2s} results:" % (label),
        for y in results:
            print " %i box (%6.3f%%)" % (y, results[y]),
        print ""
    

def getSuccessDistribution(params, threshold, defResults):
   
    successDict = {} 
    for defMod in range(0, -6, -1):
        rngDict = getRangeResults(threshold, defResults, defMod)
        successDict[defMod] = rngDict
       
    return successDict

def getRangeResults(threshold, defResults, defMod):
    results = {}
    count = 0
    maxRes = 0
    for defDiceRes in defResults:
        defRes = 0
        if defDiceRes != 0:
            defRes = defDiceRes + defMod
            
        result = 0
        if threshold > defRes:
            result = threshold - defRes

        if result > maxRes:
            maxRes = result

        count = count + 1
        if result in results:
            results[result] = results[result] + 1
        else:
            results[result] = 1

        if debug:
            print "%i vs. (%i %+i = %i) -> %i" % (
                threshold, 
                defDiceRes, defMod, defRes,
                result
                )
    
    checkCount = 0    
    for x in results.iterkeys():
        y = results[x]
        prob = (float(results[x]) / float(count)) * 100.0
        if debug:
            print "  MoS %i has %i occurances (%2.3f)" % (x, y, prob)
        results[x] = prob

        checkCount = checkCount + y
    
    for x in range(0, maxRes + 1, 1):
        if x not in results:
            results[x] = 0

    return results

def getRawOutcomes(numDice):
    outcomes = []
    # Generate the raw list that contains all possible outcomes
    for x in range(1, numDice+1):
        outcomes = appendDiceSides(outcomes, diceSides)
    
    # For each element, reduce it to a pre-modifier value
    results = []
    for x in outcomes:
        res = 0
        for y in x:
            if y == 6 and res >= 6:
                res = res + 1
            elif y > 1 and y > res:
                res = y
        results.append(res)
        if debug:
            print "Raw Outcome of %s has result %i " % (x, res)

    return results

def appendDiceSides(diceList, diceSides):

    if len(diceList) is 0:
        for x in range(1, diceSides+1):
            diceList.append([x])
        return diceList

    newList = []
    for y in diceList:
        # y is an element of list type
        for x in range(1, diceSides+1):
            z = y[:]
            z.append(x)
            newList.append(z)

    return newList

if __name__ == '__main__':
    params = {
        'threshold': 4,
        'defDice': 2,
        'dam' : 8,
        'armor': 15,
        'boxes': 3
    }

    if len(argv) >= 2:
        params['threshold'] = int(argv[1])
    if len(argv) >= 3:
        params['defDice'] = int(argv[2])
    if len(argv) >= 4:
        params['dam'] = float(argv[3])
    if len(argv) >= 5:
        params['armor'] = float(argv[4])
    if len(argv) >= 6:
        params['boxes'] = float(argv[5])

    probDict = getProbDicts(params)

